/*
 * Trireg.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */
package engine;

import java.util.*;

/**
 * This is the class for the <code>trireg</code> net construct
 * (also known as tri).
 * @see WireDescription.
 */

class Trireg extends Wire implements Executable{

	/**
	 * The strength when not in driven state
	 */
	byte notDrivStrength;

	/**
	 * The counters for turn-off delay
	 */
	int timeTillOff[];

	/**
	 * The amount of time the last value will be stored
	 */
	int storeTime;

	/**
	 * Holds the last scheduled update time
	 */
	long updateTime;

	Trireg(NameSpace ns, WireDescription desc, int type,
			int nStart, int nEnd, int expandType, byte noDrivStrength,
			Delay3 delays){
		super(ns, desc, type, nStart, nEnd, expandType, delays);
		xConsole.debug("creating a trireg");
		timeTillOff = new int[n];
		storeTime = delays.delaySpecs == 3 ? delays.delay3Typ : -1;
		updateTime = -1;
	}

	public int computeTransitionDelayFor(BitVector b){
		return computeTransitionDelayFor(this, b, delays);
	}

	/**
	 * Returns the delay for the transition from the current
	 * value (before) to a given one (after).
	 */
	public static int computeTransitionDelayFor(BitVector before,
			BitVector after,
			Delay3 delays){

		if(before.equals(after)){
			return 0;
		}

		switch(delays.delaySpecs){
		case 1:
			return delays.delay1Typ;
		case 2:
			if(after.isNull())
				return delays.delay2Typ;    // !0 -> 0
				return delays.delay1Typ;       //0 -> !0
		case 3:
			if(after.isZ())return delays.delay3Typ; // ?? -> Z
			if(after.isNull())
				return delays.delay2Typ;    // !0 -> 0
			return delays.delay1Typ;       //0 -> !0
		}

		throw new Error("uncovered case in Trireg.computeTransitionDelayFor: "
				+ before + " -> " + after);

	}

	/**
	 * The improved version here is strength - aware
	 */
	synchronized Object[] compute() throws InterpretTimeException{
		BitVector b = new BitVector(this);
		byte strengths[] = new byte[b.n];
		initToDefaults(b, strengths);
		xConsole.debug("Trireg.compute->");
		for(Enumeration e = data.elements() ; e.hasMoreElements() ; ){
			ContBitSelect bs = (ContBitSelect)e.nextElement();
			xConsole.debug("bitsel: " + bs);
			for(int i = bs.start, inc = bs.start <= bs.end ? 1 : -1
					;; i += inc){
				xConsole.debug("i = " + i);
				int index = increment == 1 ? i - lsb : lsb - i;
				int bsBit = bs.data.getBit(i);
				byte bsStrength = bs.strength(i);
				int result = Strength.reconcileDrivers(truthTable,
						strengths[index],
						bsStrength,
						b.getBit(i),
						bsBit);
				b.setBit(i, result >> 16);
				strengths[index] = (byte) (result & 0xff);
				if(i == bs.end)break;
			}
		}
		xConsole.debug("<-Trireg.compute");
		Object [] res = {b, strengths};
		return res;
	}

	/**
	 * A Strengths - aware version of attrib
	 * Note that it assumes the values are obtained from a 
	 * compute, and so they are correct - no checks are made here.
	 */
	synchronized void attrib(BitVector b, byte[] strengths)
	throws InterpretTimeException
	{
		boolean needsUpdate=false;
		for(int i = msb, index = 0 ; i <= lsb ; i += increment, index++){
			int bit = b.getBit(i);
			byte strength = Strength.getStrengthForValue(strengths[index],
					bit);
			xConsole.debug(" i = " + i + " strength = " + strength + " bit = "
					+ bit);
			if(strength == Strength.highz){
				this.strength[index] = notDrivStrength;
				timeTillOff[index] = storeTime;
				xConsole.debug("time till off : " + storeTime);
				needsUpdate = true;
			}else{
				setBit(i, bit);
				this.strength[index] = strengths[index];
				timeTillOff[index] = -1;
			}
		}
		if(updateTime < Time.oClock() && needsUpdate){
			//enable the timer:
			xConsole.debug("adding trireg updater : " + this);
			updateTime = Time.oClock();
			Time.addFinisher(0, new GenericInstruction(this));
		}
		notifyMonitors();
	}

	/**
	 * Checks to see if any bit timeout has expired, 
	 * and if so, make it X
	 */
	public synchronized void execute()
	throws InterpretTimeException
	{
		boolean modified = false, updatable = false;
		for(int i = 0 ; i < n ; i++)
			if(timeTillOff[i] >=0){
				updatable = true;
				if(timeTillOff[i] == 0){
					setb(i, X);
					modified = true;
					timeTillOff[i] = -1; //mark as unknown
				}else{
					timeTillOff[i]--;
					xConsole.debug("Till off: " + timeTillOff[i]);
				}
			}
		if(updatable){
			updateTime = Time.oClock() +1; //advance one unit
			Time.addFinisher(1, new GenericInstruction(this));
		}
		if(modified){
			xConsole.trace(desc + "<(decay)<" + this);
			notifyMonitors();
		}
	}

}













